/*
 * comPort.c
 *
 * Created: 6/23/2013 1:25:51 AM
 *  Author: Tim
 */ 

#include "comPort.h"
#include "../board/board.h"
#include "../packets/structs/msgHeaderDefs.h"
#include "../packets/CRC.h"

#if defined (__AVR_ATxmega32A4U__)
#define daqUSART			USARTD1
#define daqUSART_RXC_vect	USARTD1_RXC_vect
#define daqUSART_DRE_vect	USARTD1_DRE_vect
#define daqUSART_PR			USARTD1_PR
#else
#define daqUSART			USARTC0
#define daqUSART_RXC_vect	USARTC0_RXC_vect
#define daqUSART_DRE_vect	USARTC0_DRE_vect
#define daqUSART_PR			USARTC0_PR
#endif

FILE USB_str = FDEV_SETUP_STREAM(comPortPutC, comPortGetC, _FDEV_SETUP_RW);

buf_t bufRxD1 = {&bufRxD1.data[0],&bufRxD1.data[bufferSize-1],&bufRxD1.data[0],&bufRxD1.data[0],0};
buf_t bufTxD1 = {&bufTxD1.data[0],&bufTxD1.data[bufferSize-1],&bufTxD1.data[0],&bufTxD1.data[0],0};

void comPortPutC(uint8_t c)
{
	cli();
	bufferWrite(&bufTxD1,c);
	daqUSART.CTRLA &= ~USART_DREINTLVL_gm;
	daqUSART.CTRLA |= USART_DREINTLVL_LO_gc;	//setting this bit should cause the DRE interrupt to be triggered
	sei();
}

uint8_t comPortGetC()
{
	uint8_t temp;
	cli();
	temp = bufferRead(&bufRxD1);
	sei();
	return temp;
}
	
void comPortPut(uint8_t *packet,uint16_t packetLength)
{
	cli();
	uint16_t i;
	for(i=0;i<packetLength;i++)
	{
		bufferWrite(&bufTxD1,packet[i]);
	}
	daqUSART.CTRLA &= ~USART_DREINTLVL_gm;
	daqUSART.CTRLA |= USART_DREINTLVL_LO_gc;	//setting this bit should cause the DRE interrupt to be triggered
	sei();
}

//packet should be the address to an array containing all of the packet information.
//the address that should be inserted into the function should be the base address
//this function takes into account the 2 byte offset incurred from previously reading
//the packet header as well as the msg header bytes.
uint8_t comPortGet(uint8_t *packet)
{

	uint8_t i;
	uint8_t packetLength;

	uint8_t *virtualBufferHead;
	uint8_t *virtualBufferTail;
	
	if(dataInBuf(&bufRxD1)) packet[0] = bufferRead(&bufRxD1);
	else return 0;

	//read data from the port for as long as it is present/a packet header has not been found
	while(packet[0] != 0x55) 
	{
		if(dataInBuf(&bufRxD1)) packet[0] = bufferRead(&bufRxD1);
		else					return 0;	//no more data to read 	
	}	
	
	virtualBufferHead = bufRxD1.head;	//preserve head of ring buffer
	virtualBufferTail = bufRxD1.tail;  //preserve tail of ring buffer
	
	if(timeOut())	return 0;
	else			packet[1] = bufferRead(&bufRxD1);	
	
	if((packetLength = packetSizeLookup(packet[1])));
	else return 0;
	
	//i is equal to 2 initially since the packet header and the msg header
	//are used respectively to signify the existance of the message,
	//and to specify the number of bytes are contained in the message;
	
	for(i=2;i<packetLength;i++)
	{
		
		if(timeOut())
		{
			//there was not enough data in the buffer for the prescribed packet.
				
			bufRxD1.head = virtualBufferHead;
			bufRxD1.tail = virtualBufferTail;
			return 0;
		}
		else packet[i] = bufferRead(&bufRxD1);
	}
	//check the datas crc.
	//If it checks out return a 1
	//this signals that the packet is ready for processing.
	if (crcCheck(packet,packetLength)) 
	{
		return 1;
	}
	else
	{
		//data was bad, so the head and tail values will be restored
		//this is done specifically to prevent glossing over a message.
		bufRxD1.head = virtualBufferHead;
		bufRxD1.tail = virtualBufferTail;
		
		return 0;
	}
}

uint8_t bufferRead(buf_t *buffer){
	if (buffer->tail == buffer->bufferEnd){
		uint8_t temp = *buffer->tail;
		buffer->tail = buffer->bufferBegin;
		return temp;
	}
	else 
	{
		return *buffer->tail++;
	}
}

void bufferWrite(buf_t *buffer, char c){
	if(buffer->head == buffer->bufferEnd){
		*buffer->head = c;
		buffer->head = buffer->bufferBegin;
	}
	else *buffer->head++ = c;
}

uint8_t dataInBuf(buf_t *buffer){
	if(buffer->head == buffer->tail) return 0;	//no data to be read
	else return 1;
}

ISR(daqUSART_RXC_vect){
	cli();
	bufferWrite(&bufRxD1,daqUSART.DATA);				//when data has arrived via the usart it is immediately stored in the ring buffer
	PORTC.OUTTGL = 1;
	sei();
}

ISR(daqUSART_DRE_vect)
{
	cli();
	if(dataInBuf(&bufTxD1))
	{
		daqUSART.DATA = bufferRead(&bufTxD1);		//this is used to send packets of data in quick bursts
	}
	else daqUSART.CTRLA &= ~USART_DREINTLVL_gm;
	sei();
}

//initialization functions for the uarts
void comPortInit()
{
	prEnable(daqUSART_PR);
	prEnable(RTC_PR);
	
	/*Real time Clock init*/

#ifndef DEBUG
	//enables the RTC with the precision 32.768Khz internal clock
	CLK.RTCCTRL = CLK_RTCSRC_RCOSC32_gc | CLK_RTCEN_bm;

	//setup RTC peripheral
	RTC.CTRL = RTC_PRESCALER_OFF_gc;		//keep off until in use for delays
	while(RTC.STATUS & RTC_SYNCBUSY_bm);	//wait for the RTC module to respond	
	RTC.PER = 33;//33							//~1ms timeout period
	while(RTC.STATUS & RTC_SYNCBUSY_bm);	//wait for the RTC module to respond
	RTC.COMP = 33;//33							//set to the same value as the period
	while(RTC.STATUS & RTC_SYNCBUSY_bm);	//wait for the RTC module to respond
#endif	
	
	//usart settings
	daqUSART.CTRLA |= USART_RXCINTLVL_MED_gc;
	daqUSART.CTRLB = USART_CTRLB_1M;
	daqUSART.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_CHSIZE_8BIT_gc;
	daqUSART.BAUDCTRLA = USART_BAUDCTRLA_1M;
	daqUSART.BAUDCTRLB = USART_BAUDCTRLB_1M;
	
	//system level interrupt enables
	PMIC.CTRL |= PMIC_MEDLVLEN_bm | PMIC_LOLVLEN_bm;
	
}

uint8_t timeOut()
{
	//zero the rtc counter
#ifndef DEBUG
	RTC.CNT = 0;
	while(RTC.STATUS & RTC_SYNCBUSY_bm);			
	//turn on counter
	RTC.CTRL = RTC_PRESCALER_DIV1_gc;
	while(RTC.STATUS & RTC_SYNCBUSY_bm);
	while(!dataInBuf(&bufRxD1))
	{
		if(RTC.INTFLAGS & RTC_COMPIF_bm)	return 1;
	}
	
	RTC.CTRL = RTC_PRESCALER_OFF_gc;
	while(RTC.STATUS & RTC_SYNCBUSY_bm);
#endif
	return 0;
}